//
//  Main.cpp
//  BaseDemo
//
//  Created by jiuyv on 2019/5/8.
//  Copyright © 2019 Julija. All rights reserved.
//

#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"
#include <math.h>

#include <glut/glut.h>
#include <stdio.h>


GLShaderManager shaderManager;
GLMatrixStack   modelViewMatrix;
GLMatrixStack   projectionMatrix;
GLFrame         cameraFrame;
GLFrame         objectFrame;
//投影矩阵
GLFrustum       viewFrustum;

//容器类
GLBatch         pointBatch;
GLBatch         lineBatch;
GLBatch         lineStripBatch;
GLBatch         lineLoopBatch;
GLBatch         triangleBatch;
GLBatch         triangleStripBatch;
GLBatch         triangleFanBatch;

//几何变换管道
GLGeometryTransform transformPipeline;

GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };


// 跟踪效果步骤
int nStep = 0;


void ChangeSize(int w, int h){
    
    glViewport(0, 0, w, h);
    
    //创建投影矩阵,并载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35, float(w)/float(h), 10, 500);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    //调用顶部载入单元矩阵
    modelViewMatrix.LoadIdentity();
}

void SetupRC(){
    //设置背景颜色 蓝色
    glClearColor(0.04, 0.42, 1.00, 1.00);
    
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    
    //开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    //设置变换管线以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    cameraFrame.MoveForward(-15.0f);
    
    //定义3个点 三角形状
    GLfloat vCoast[9] = {
        3,3,0,0,3,0,3,0,0
    };
    
    //点形式
    pointBatch.Begin(GL_POINTS, 3);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    //线形式
    lineBatch.Begin(GL_LINES, 3);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    //线段形式
    lineStripBatch.Begin(GL_LINE_STRIP, 3);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    //线环形式
    lineLoopBatch.Begin(GL_LINE_LOOP, 3);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();
    
    //通过三角形搭建金字塔
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f
    };
    
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
    //三角形扇形 六边形
    GLfloat vPoints[100][3];
    int nVerts = 0;
    //半径
    GLfloat r = 3.0f;
    //原点(x,y,z) = (0,0,0);
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    
    for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
        nVerts++;
        //x点坐标 cos(angle) * 半径
        vPoints[nVerts][0] = float(cos(angle)) * r;
        //y点坐标 sin(angle) * 半径
        vPoints[nVerts][1] = float(sin(angle)) * r;
        //z点的坐标
        vPoints[nVerts][2] = -0.5f;
    }
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;
    
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    //三角形带
    int iCounter = 0;
    //半径
    GLfloat radius = 3.0f;
    for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f){
        //或许圆形的顶点的X,Y
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        
        //绘制2个三角形（他们的x,y顶点一样，只是z点不一样）
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = -0.5;
        iCounter++;
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
    }
    // 关闭循环
    //结束循环，在循环位置生成2个三角形
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = -0.5;
    iCounter++;
    
    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;
    
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}

void DrawWireFrameBatch(GLBatch* batch){
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vGreen);
    batch->Draw();
    
    /*
     glEnable(GLenum cap) 开启各种功能,功能由参数决定
     glDisable(GLenum cap) 关闭各种功能,功能由参数决定
     
     glEnable() 不能写在glBegin() 和 glEnd()中间
     GL_POLYGON_OFFSET_LINE : 根据函数glPolygonOffset的设置，启用线的深度偏移
     GL_LINE_SMOOTH : 执行后 过滤线点的锯齿
     GL_BLEND : 颜色混合模式,如半透明
     GL_DEPTH_TEST : 深度测试,根据坐标远近自动隐藏被遮挡的图形
     
     */
    
    //画黑色边框
    glPolygonOffset(-1.0f, -1.0f);
    glEnable(GL_POLYGON_OFFSET_LINE);
    //画反锯齿,是黑边好看些
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    glLineWidth(2.5f);
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    batch->Draw();
    
    //复原原本设置
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    
}
void RenderScene(void){
    //清除一个或一组特定缓冲区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
   
    //压栈
    modelViewMatrix.PushMatrix();
    
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果存储在堆栈顶部
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    //使用GetMatrix函数可以获取矩阵堆栈顶部的值
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果存储在堆栈顶部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    
    switch (nStep) {
        case 0:
            glPointSize(5.0f);
            pointBatch.Draw();
            glPointSize(1.0f);
            break;
        case 1:
            glLineWidth(4.0f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2:
            glLineWidth(4.0f);
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3:
            glLineWidth(4.0f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrawWireFrameBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFrameBatch(&triangleFanBatch);
            break;
        case 6:
            DrawWireFrameBatch(&triangleStripBatch);
            break;
    }
    
    
    
    modelViewMatrix.PopMatrix();
    
    //将在后台缓冲进行渲染,结束时交换到前台
    glutSwapBuffers();
    
}

void SpecicalKeys(int key, int x, int y){
    if (key == GLUT_KEY_UP) {
        objectFrame.RotateWorld(m3dDegToRad(-5.f), 1.0f, 0, 0);
    }
    if (key == GLUT_KEY_DOWN) {
        objectFrame.RotateWorld(m3dDegToRad(5.f), 1.0f, 0, 0);
    }
    if (key == GLUT_KEY_LEFT) {
        objectFrame.RotateWorld(m3dDegToRad(-5.f), 0, 1.0f, 0);
    }
    if (key == GLUT_KEY_RIGHT) {
        objectFrame.RotateWorld(m3dDegToRad(5.f), 0, 1.0f, 0);
    }
    
    glutPostRedisplay();
}
void KeyPressFunc(unsigned char key, int x, int y){
    if (key == 32) {
        nStep++ ;
        if (nStep > 6) {
            nStep = 0;
        }
        switch (nStep) {
            case 0:
                
                break;
            case 1:
                
                break;
            case 2:
                
                break;
            case 3:
                
                break;
            case 4:
                
                break;
            case 5:
                
                break;
        }
        
        glutPostRedisplay();
        
    }
}

int main(int argc, char* argv[]){
    
    //设置当前工作目录,针对MAC OS X
    gltSetWorkingDirectory(argv[0]);
    
    //初始化GLUT库
    glutInit(&argc, argv);
    
    //初始化缓冲区窗口,其中标志GLUT_DOUBLE GLUT_RGBA GLUT_DEPTH GLUT_STENCIL 分别指 双缓冲区 颜色缓冲区 深度缓冲区 模板缓冲区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置窗口尺寸
    glutInitWindowSize(600, 600);
    //window名称
    glutCreateWindow("BaseDemo");
    //注册回调函数
    //改变窗口尺寸时回调
    glutReshapeFunc(ChangeSize);
    //窗口显示时回调
    glutDisplayFunc(RenderScene);
    //特殊键位函数(上下左右)
    glutSpecialFunc(SpecicalKeys);
    //点击空格时回调
    glutKeyboardFunc(KeyPressFunc);
    
    //驱动程序初始化过程中是否出现问题
    GLenum error = glewInit();
    if (GLEW_OK != error) {
        fprintf(stderr, "glew error:%s\n",glewGetErrorString(error));
        return 1;
    }
    
    SetupRC();
    
    glutMainLoop();
    
    return 0;
}

